﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ESRI.ArcGIS.Geodatabase;
using System.Runtime.InteropServices;
using EAGL.Explore;

namespace EAGL.Data
{
    /// <summary>
    /// Synchronizes data between any two ITable instances.
    /// </summary>
    public class TableSynchronizer
    {
        private ITable source;
        private ITable destination;
        private string srcPrimaryFieldName;
        private string desPrimaryFieldName;
        private SyncResult SyncResult;
        private ICursor srcCrsr;
        private ICursor desCrsr;


        private IField getSrcPrimaryField()
        {
            IField f = null;
            int x = 0;
            string s =  srcPrimaryFieldName;
            x = source.FindField(s);
            f = source.Fields.get_Field(x);
            return f;
        }

        private IField getDesPrimaryField()
        {
            IField f = null;
            int x = 0;
            string s = desPrimaryFieldName;
            x = source.FindField(s);
            f = source.Fields.get_Field(x);
            return f;
        }

        
        public TableSynchronizer(ITable source, ITable destination, string srcPrimaryFieldName, string desPrimaryFieldName)
        {
            this.source = source;
            this.destination = destination;
            this.srcPrimaryFieldName = srcPrimaryFieldName;
            this.desPrimaryFieldName = desPrimaryFieldName;
        }

        public IRow getRow(ITable table, IField searchField, object value)
        {
            IQueryFilter qf = new QueryFilterClass();
            string clause = "";
            if (searchField.Type == esriFieldType.esriFieldTypeString)
                clause = string.Format("{0} = '{1}'", searchField.Name, value);
            else if ( searchField.Type == esriFieldType.esriFieldTypeInteger || 
                      searchField.Type == esriFieldType.esriFieldTypeSingle ||
                      searchField.Type == esriFieldType.esriFieldTypeDouble)
                clause = string.Format("{0} = {1}", searchField.Name, value);
            
            qf.WhereClause = clause;
            // must not use recycling
            ICursor crsr = table.Search(qf, false);
            IRow row = crsr.NextRow();
            Marshal.ReleaseComObject(crsr);
            return row;
        }

        private IRowBuffer getBuffer(ITable table, IRow row)
        {
            IRowBuffer rb = table.CreateRowBuffer();
            int i = 0, c = rb.Fields.FieldCount;
            int oidIndex = table.FindField(table.OIDFieldName);
            while (i < c)
            {
                if (i != oidIndex)
                    rb.set_Value(i, row.get_Value(i));
                i++;
            }
            return rb;
        }

        private SyncResult sync()
        {
            IRow desRow = null;
            IRow srcRow = null;
            SyncResult result = new SyncResult();
            IField srcPF = getSrcPrimaryField();
            IField desPF = getDesPrimaryField();
            int srcPFIndex = source.FindField(srcPF.Name);
            int desPFIndex = destination.FindField(desPF.Name);
            int srcOIDIndex = source.FindField(source.OIDFieldName);
            int desOIDIndex = destination.FindField(destination.OIDFieldName);

            ////phase 1: scan the destination cursor, if you find the same row in source update destination, else delete row
            //srcCrsr = source.Search(null, false);
            //desCrsr = destination.Update(null, false);
            //while ( (desRow = desCrsr.NextRow()) != null)
            //{
            //    srcRow = getRow(source, srcPF, desRow.get_Value(desPFIndex));
            //    if (srcRow != null)
            //    {
            //        desCrsr.UpdateRow(srcRow);
            //        result.UpdateCount++;
            //    }
            //    else
            //    {
            //        desCrsr.DeleteRow();
            //        result.DeleteCount++;
            //    }
            //}

            //Marshal.ReleaseComObject(srcCrsr);
            //Marshal.ReleaseComObject(desCrsr);

            // phase 2: scan the source cursor, add all rows the exists in source and doesn't exist in destination
            srcCrsr = source.Search(null, false);
            desCrsr = destination.Insert(true);
            while ((srcRow = srcCrsr.NextRow()) != null)
            {
                desRow = getRow(destination, desPF, srcRow.get_Value(srcPFIndex));
                
                if (desRow == null)
                {
                    IRowBuffer rb = getBuffer(destination, srcRow);
                    desCrsr.InsertRow(rb);
                    result.InsertCount++;
                }
                
            }
            desCrsr.Flush();

            Marshal.ReleaseComObject(srcCrsr);
            Marshal.ReleaseComObject(desCrsr);

            return result;

        }

        private bool checkPrimaryField()
        {
           return true; 
        }

        public string SourcePrimaryField
        {
            get { return srcPrimaryFieldName; }
            set { srcPrimaryFieldName = value; }
        }

        public string DestinationPrimaryField
        {
            get { return desPrimaryFieldName; }
            set { desPrimaryFieldName = value; }
        }

        public SyncResult Synchronize()
        {
            return sync();
        }


       
    }
}
